import torch,os,time,aimbot_pid,cv2
import win32api,win32con,win32gui,pyautogui,threading
import numpy as np
import tkinter as tk
from tkinter.messagebox import askquestion, showinfo

# 自行设置参数
KP = 0
KI = 0
KD = 0
GAME_NAME = "Counter-Strike Source"
AIMBOT_HOT_KEY = "E"
PROCESSING = True
CONF = 0.2
AIMBOT_RANGE = 999
WINDOW_TITLE = "CSS AI自瞄程序"
WINDOW_SIZE = "640x480"

model = None
pid = None

loading_state = {
    "config":False,
    "yolov5":False,
    "pid":False,
    "game":False
}

# 读取配置文件
def load_config():
    '''
    读取配置文件，无返回值
    '''
    global KP,KI,KD,AIMBOT_HOT_KEY,CONF,AIMBOT_RANGE
    local_dir = os.path.split(os.path.realpath(__file__))[0]+'\\'
    data = open(local_dir+"config.ini","r",encoding='utf-8').readlines()
    KP = float(data[1].split("=")[1])
    KI = float(data[2].split("=")[1])
    KD = float(data[3].split("=")[1])
    CONF = float(data[4].split("=")[1])
    AIMBOT_RANGE = float(data[5].split("=")[1])
    AIMBOT_HOT_KEY = data[7].split("=")[1]

#YOLOV5初始化
def load_yolov5():
    '''
    载入YOLOV5x模型，返回载入时间
    '''
    global model
    local_dir = os.path.split(os.path.realpath(__file__))[0]+'\\'
    t1 = time.time()
    model = torch.hub.load(local_dir+"yolov5","custom",local_dir+"weights/best.pt",source="local")
    return time.time() - t1

#pid初始化
def load_pid():
    '''
    载入PID算法，无返回值
    '''
    global pid
    pid = aimbot_pid.PID(KP,KI,KD,0,0,0,0,0)

def check_game():
    #获得游戏HWD
    hwnd = win32gui.FindWindow(None,GAME_NAME)
    if (hwnd == 0):
        return False
    return True

def main_process():
    '''
    程序主循环，自瞄程序核心代码
    '''
    window_loading.destroy()
    tk.Tk().withdraw()  # 取消默认窗口
    show_result = askquestion("边框可视化","是否开启边框可视化？")
    info_text = '''
    自瞄范围：{}
    自瞄置信度：{}
    自瞄快捷键：{}

    点击 确定 后请切回游戏，按下 自瞄快捷键 即可开启自瞄。
    游戏窗口关闭后本程序自动关闭。
    
    '''.format(AIMBOT_RANGE,CONF,AIMBOT_HOT_KEY)
    showinfo("说明",info_text)
    #进入处理循环
    while(PROCESSING):
        img = np.asarray(pyautogui.screenshot())
        img = cv2.cvtColor(np.asarray(img),cv2.COLOR_RGB2BGR)
        t1 = time.time()
        results = model(img).pandas().xyxy[0]
        print("用时：{}s".format(time.time() - t1))
        #处理结果
        xmins = results["xmin"]
        ymins = results["ymin"]
        xmaxs = results["xmax"]
        ymaxs = results["ymax"]
        confs = results["confidence"]
        names = results["name"]
        #aimbot
        for xmin,ymin,xmax,ymax,conf,name in zip(xmins, ymins, xmaxs, ymaxs, confs, names):
            #显示
            if (show_result == "yes"):
                t = ((xmax+xmin)/2,(ymin+ymax)/2)
                img = cv2.circle(img,(int(t[0]),int(t[1])),5,(0,0,255),-1)
                img = cv2.rectangle(img,(int(xmin),int(ymin)),(int(xmax),int(ymax)),(255,0,0),2)
                cv2.imwrite("1.jpg",img)
                img = cv2.resize(img,(640,480))
                cv2.imshow("showing",img)
                cv2.resizeWindow("showing",640,480)
                if cv2.waitKey(25) & 0xFF == ord("p"):
                    cv2.destroyAllWindows()
                    break
            # 开启自瞄
            if win32api.GetAsyncKeyState(ord(AIMBOT_HOT_KEY)):
                if (conf < CONF):
                    continue
                a = win32api.GetCursorPos()
                #计算t
                t = ((xmax+xmin)/2,(ymin+ymax)/2)
                #判断是否处于瞄准范围
                if (aimbot_pid.distance(t,a) <= AIMBOT_RANGE):
                    if (a == t):
                        continue
                    add_v,dis = pid.process(t,a)
                    add_v = abs(add_v)
                    #计算dx，dy
                    d = (a[0]-t[0],a[1]-t[1])
                    k = dis/add_v
                    m_dx,m_dy = abs(d[0]/k),abs(d[1]/k)
                    #判断象限
                    if (d[0] >=0 and d[1] >= 0):
                        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE,-int(m_dx),-int(m_dy),0,0)
                    elif (d[0] <=0 and d[1] >=0):
                        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE,int(m_dx),-int(m_dy),0,0)
                    elif (d[0] >=0 and d[1] <=0):
                        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE,-int(m_dx),int(m_dy),0,0)
                    elif (d[0] <=0 and d[1] <=0):
                        win32api.mouse_event(win32con.MOUSEEVENTF_MOVE,int(m_dx),int(m_dy),0,0)
        # 检查游戏是否运行
        if not check_game:
            showinfo("说明","游戏已结束运行，程序自动退出。")
            break
# GUI1

# 载入界面
window_loading = tk.Tk()
window_loading.title(WINDOW_TITLE)
window_loading.geometry(WINDOW_SIZE)

# 字符串创建
config_str = tk.StringVar()
yolov5_str = tk.StringVar()
pid_str = tk.StringVar()
game_state_str = tk.StringVar()
config_str.set("正在载入配置文件...")
yolov5_str.set("正在载入YOLOV5x模型...")
pid_str.set("正在载入PID算法...")
game_state_str.set("正在检测游戏状态...")

# 标签
config_label = tk.Label(window_loading,textvariable=config_str,font=('Arial',12),width=30,height=2)
yolov5_label = tk.Label(window_loading,textvariable=yolov5_str,font=('Arial',12),width=30,height=2)
pid_label = tk.Label(window_loading,textvariable=pid_str,font=('Arial',12),width=30,height=2)
game_state_label = tk.Label(window_loading,textvariable=game_state_str,font=('Arial',12),width=30,height=2)

# 按钮 初始化不点击
start_button = tk.Button(window_loading,text="开始",width=15,height=2,command=main_process,state=tk.DISABLED)

# 布局
config_label.pack()
yolov5_label.pack()
pid_label.pack()
game_state_label.pack()
start_button.pack()

def loading():
    '''
    载入各项配置
    '''
    # 载入
    load_config()
    loading_state["config"] = True
    config_str.set("配置文件载入完成！")
    config_label["fg"] = "green"
    spend_time = load_yolov5()
    if model == None:
        yolov5_str.set("YOLOV5x模型载入失败！")
        yolov5_label["fg"] = "red"
    else:
        loading_state["yolov5"] = True
        yolov5_str.set("YOLOV5x模型载入完成！\n耗时：{}s".format(round(spend_time,2)))
        yolov5_label["fg"] = "green"
    load_pid()
    if pid == None:
        pid_str.set("PID算法载入失败！")
        pid_label["fg"] = "red"
    else:
        loading_state["pid"] = True
        pid_str.set("PID算法载入完成！")
        pid_label["fg"] = "green"
    if (check_game()):
        loading_state["game"] = True
        game_state_str.set("游戏状态检测完成！\n游戏已启动！")
        game_state_label["fg"] = "green"
    else:
        game_state_str.set("游戏状态检测完成！\n游戏未启动！")
        game_state_label["fg"] = "red"
    
    if loading_state["config"] and loading_state["yolov5"] and loading_state["pid"] and loading_state["game"]:
        start_button["state"] = tk.NORMAL
    else:
        tips_label = tk.Label(window_loading,text="无法启动程序，请确保所有项目均显示载入完成。",font=('Arial',12),width=38,height=2,fg="red")
        tips_label.pack()

t = threading.Thread(target=loading)
t.start()
window_loading.mainloop()